package com.zkx.laolaiyi.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zkx.laolaiyi.annotation.AutoFill;
import com.zkx.laolaiyi.constant.ExceptionMsgConstant;
import com.zkx.laolaiyi.constant.RedisConstants;
import com.zkx.laolaiyi.constant.ResultCodeConstant;
import com.zkx.laolaiyi.constant.ResultMsgConstant;
import com.zkx.laolaiyi.dto.UserDTO;
import com.zkx.laolaiyi.entity.User;
import com.zkx.laolaiyi.enumeration.DBOperationType;
import com.zkx.laolaiyi.exceptions.RedisUnknownException;
import com.zkx.laolaiyi.exceptions.UserIsExistedException;
import com.zkx.laolaiyi.mapper.UserMapper;
import com.zkx.laolaiyi.result.Result;
import com.zkx.laolaiyi.service.IUserService;
import jodd.util.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.aop.framework.AopContext;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;

@Service
@RequiredArgsConstructor
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    private final RedissonClient redissonClient;
    private final StringRedisTemplate stringRedisTemplate;
    private final PasswordEncoder passwordEncoder;
    private Long currentUid;

    @Override
    public Result<String> register(UserDTO userDTO) {
        IUserService proxy = (IUserService) AopContext.currentProxy();
        RLock lock = redissonClient.getLock(RedisConstants.LOCK_UID_KEY);
        // 获取分布式锁
        boolean success = lock.tryLock();
        if (success) {
            try {
                // 将userDTO转化为user
                User user = BeanUtil.copyProperties(userDTO, User.class);
                if (proxy.saveUser(user)) {
                    log.info("用户注册成功，用户uid为：{}", stringRedisTemplate.opsForValue().get(RedisConstants.USER_UID_CURRENT_KEY));
                    stringRedisTemplate.opsForValue().increment(RedisConstants.USER_UID_CURRENT_KEY);
                    return Result.success();
                }
            } catch (UserIsExistedException e) {
                log.error("用户名已存在，原因：{}", e.getMessage(), e);
                return Result.error(ResultCodeConstant.USER_IS_EXISTED, ResultMsgConstant.USER_IS_EXISTED);
            } catch (RedisUnknownException e) {
                log.error("未从redis中查询到当前uid，原因：{}", e.getMessage(), e);
            } catch (Exception e) {
                log.error("用户注册时产生未知异常，原因：{}", e.getMessage(), e);
            } finally {
                lock.unlock();
            }
        }
        return Result.error(ResultCodeConstant.REGISTER_FAILED, ResultMsgConstant.REGISTER_FAILED);
    }

    @Transactional
    @AutoFill(DBOperationType.LOGIN_INSERT)
    public boolean saveUser(User user) throws UserIsExistedException, RedisUnknownException {
        // 根据username查询用户
        User retUser = lambdaQuery().eq(User::getUsername, user.getUsername()).one();
        // 如果存在抛出UserIsExistedException异常
        if (!ObjectUtil.isNull(retUser)) {
            throw new UserIsExistedException(ExceptionMsgConstant.USER_IS_EXISTED);
        }
        // 从redis中获取当前项目的用户uid
        String currentUidStr = stringRedisTemplate.opsForValue().get(RedisConstants.USER_UID_CURRENT_KEY);
        if (StringUtil.isBlank(currentUidStr)) {
            throw new RedisUnknownException(ExceptionMsgConstant.REDIS_UNKNOWN_EXCEPTION);
        }

        /*
            当saveUser()执行完成后服务若宕机，redis中的uid可能没来得及自增1，
            所以需要在这里判断是否存在该uid的用户
        */
        Long currentUid = Long.valueOf(currentUidStr);
        User checkUidIsValid = lambdaQuery().eq(User::getUid, currentUid).one();
        while (!ObjectUtil.isNull(checkUidIsValid)) {
            currentUid++;
            checkUidIsValid = lambdaQuery().eq(User::getUid, currentUid).one();
        }

        // 补全user属性
        user.setUid(currentUid);
        user.setPassword(passwordEncoder.encode(user.getPassword()));
//        LocalDateTime now = LocalDateTime.now();
//        user.setCreateTime(now);
//        user.setUpdateTime(now);
        // 昵称默认为username
        if (user.getNickname() == null) {
            user.setNickname(user.getUsername());
        }
        return save(user);
    }
}
